package cn.doitedu.ml.demo

import org.apache.spark.ml
import org.apache.spark.ml.linalg
import org.apache.spark.ml.linalg.Vectors

/**
  * @date: 2020/2/17
  * @site: www.doitedu.cn
  * @author: hunter.d 涛哥
  * @qq: 657270652
  * @description:
  * 向量之间的距离、相似度计算demo
  */
object VectorSimilarityDemo {

  def main(args: Array[String]): Unit = {


    val v1: linalg.Vector = Vectors.dense(Array(1.0, 2.0, 3.0))
    val v2: linalg.Vector = Vectors.dense(Array(2.0, 4.0, 6.0))

    // 用欧氏距离衡量的这两个向量的相似度为 0.21
    val eudi1 = eudiDistance1(v1,v2)
    val eudi2 = eudiDistance2(v1,v2)

    println("用欧式距离衡量的v1-v2相似度,手撕版：" + 1/(1+eudi1))
    println("用欧式距离衡量的v1-v2相似度,工具版：" + 1/(1+eudi2))


    val cos = cosSimilarity(v1,v2)
    println("用余弦相似度衡量的v1-v2相似度：" + cos)


  }


  // 给定两个向量，返回它们的欧氏距离----手撕版
  def eudiDistance1(v1: linalg.Vector, v2: linalg.Vector): Double = {
    // 求v1和v2之间的欧氏距离
    val v1Array: Array[Double] = v1.toArray
    val v2Array: Array[Double] = v2.toArray

    // Array[1.0,2.0,3.0]
    // Array[2.0,4.0,6.0]

    val sqrt = v1Array.zip(v2Array) // Array[(1,2),(2,4),(3,6)]
      .map(tp => Math.pow(tp._1 - tp._2, 2)) // Array[1,4,9]
      .sum

    Math.pow(sqrt, 0.5)
  }

  // 给定两个向量，返回它们的欧氏距离 --- 调现成工具版
  def eudiDistance2(v1: linalg.Vector, v2: linalg.Vector): Double = {
    Math.pow(Vectors.sqdist(v1,v2),0.5)
  }

  // 给定两个向量，返回它们的余弦相似度
  def cosSimilarity(v1: linalg.Vector, v2: linalg.Vector):Double = {

    val v1arr = v1.toArray
    val v2arr = v2.toArray

    // 求v1的模平方
    val m1: Double = v1arr.map(Math.pow(_,2)).sum
    // 求v2的模平方
    val m2: Double = v2arr.map(Math.pow(_,2)).sum

    // 求v1和v2的点乘
    val innerProduct: Double = v1arr.zip(v2arr).map(tp=>tp._1*tp._2).sum


    innerProduct/Math.pow(m1*m2,0.5)

  }





}
